use core::fmt::{Display, Formatter};
use derive_builder::Builder;
use serde::{Deserialize, Serialize};
use std::collections::HashMap;

use crate::error::Error;
use crate::modeling::Value;

#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq)]
pub enum ChatApiVersion {
    #[default]
    OpenAIChat,
}

#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq)]
#[serde(rename_all = "lowercase")]
pub enum ChatRole {
    #[default]
    User,
    System,
    Assistant,
}

#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq)]
pub struct ChatProvider {
    pub version: ChatApiVersion,
    pub url: String,
    pub token_key: Option<String>,
    pub token: Option<String>,
    pub request_jmespath: Option<String>,
    pub response_jmespath: Option<String>,
    pub peer: Option<String>,
    pub options: HashMap<String, String>,
}

#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq)]

pub struct ChatMessageContentImageUrl {
    pub url: String,
}

#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
#[serde(tag = "type")]
pub enum ChatMessageContent {
    #[serde(rename = "text")]
    Text { text: String },
    #[serde(rename = "image_url")]
    Image {
        image_url: ChatMessageContentImageUrl,
    },
}

impl Default for ChatMessageContent {
    fn default() -> Self {
        ChatMessageContent::Text {
            text: String::new(),
        }
    }
}

#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq)]

pub struct ChatMessage {
    pub content: Vec<ChatMessageContent>,
    pub role: ChatRole,
}

#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq)]
pub struct ChatOptions {
    pub provider: Option<ChatProvider>,
    pub model: Option<String>,
    pub extensions: Option<HashMap<String, String>>,
    pub timeout: Option<u32>,
    pub prompt_enhance: Option<bool>,
}

impl ChatMessage {
    pub fn system(content: String) -> ChatMessage {
        Self {
            content: vec![ChatMessageContent::Text { text: content }],
            role: ChatRole::System,
        }
    }
    pub fn user(content: String) -> ChatMessage {
        Self {
            content: vec![ChatMessageContent::Text { text: content }],
            role: ChatRole::User,
        }
    }
    pub fn assistant(content: String) -> ChatMessage {
        Self {
            content: vec![ChatMessageContent::Text { text: content }],
            role: ChatRole::Assistant,
        }
    }
}

impl TryFrom<Value> for ChatMessage {
    type Error = anyhow::Error;

    fn try_from(value: Value) -> Result<Self, Self::Error> {
        let serde_value: serde_json::Value = value.try_into()?;
        let message = serde_json::from_value(serde_value)?;
        Ok(message)
    }
}

impl TryInto<Value> for ChatMessage {
    type Error = anyhow::Error;

    fn try_into(self) -> Result<Value, Self::Error> {
        Value::try_from(serde_json::json!(self))
    }
}

impl ChatProvider {
    pub fn new(
        url: String,
        token_key: Option<String>,
        token: Option<String>,
    ) -> anyhow::Result<Self> {
        Ok(Self {
            version: ChatApiVersion::OpenAIChat,
            url: url,
            token_key: token_key,
            token: token,
            request_jmespath: None,
            response_jmespath: None,
            peer: None,
            options: HashMap::new(),
        })
    }

    pub fn new_with_peer(
        peer: String,
        url: String,
        token_key: Option<String>,
        token: Option<String>,
    ) -> anyhow::Result<Self> {
        Ok(Self {
            version: ChatApiVersion::OpenAIChat,
            url: url,
            token_key: token_key,
            token: token,
            request_jmespath: None,
            response_jmespath: None,
            peer: Some(peer),
            options: HashMap::new(),
        })
    }
}

impl ChatOptions {
    pub fn new() -> anyhow::Result<Self> {
        Ok(Self {
            provider: None,
            model: None,
            extensions: None,
            timeout: None,
            prompt_enhance: None,
        })
    }
}
